from decimal import Decimal

from django.core.mail import EmailMultiAlternatives
from django.utils import timezone
from django_tenants.utils import schema_context

from apps.booking.helpers.create_booking import get_tiers
from apps.booking.models import Appointment, BookingPrice, Booking, increment_booking_number, AppointmentClientServices
# from apps.booking.serializers.AppointmentCreateSerializer import validate_client_in_booking_weekly_repeat, \
#     validate_provider_in_booking_weekly_repeat
# from apps.booking.serializers.BookingSerializer import get_tiers
from datetime import timedelta, datetime
from django.conf import settings

from apps.booking.telehealth import create_meeting, create_zoom_meeting
from apps.booking.validations.update_appointment import validate_provider_in_booking_weekly_repeat, \
    validate_client_in_booking_weekly_repeat
from apps.location.models import RoomPrice, Room, AdminBookingDetails


def appointment_json(start_date, instance, i=None):
    return Appointment(
        start_date=start_date,
        is_recurrent_of=instance,
        start_time=instance.start_time,
        end_time=instance.end_time,
        booking=instance.booking,
        provider_location=instance.provider_location,
        weekly_repeat=instance.weekly_repeat,
        zoom=instance.zoom,
        google=instance.google,
        telehealth=instance.telehealth,
        select_dates=instance.select_dates,
        selected_dates=instance.selected_dates,
        telehealth_zoom_id=None,
        telehealth_google_id=None,
        number_of_weeks=((instance.number_of_weeks - i) if (i and instance.number_of_weeks > 0) and instance.weekly_repeat is True else 0)
    )


def repeating_telehealths_meetings_link(appointment):
    if appointment.telehealth is True:
        if appointment.zoom:
            appointment.telehealth_zoom_id = create_zoom_meeting(
                appointment.provider,
                appointment.start_date,
                appointment.start_time,
                appointment.end_time)
        if appointment.google:
            appointment.telehealth_google_id = create_meeting(
                appointment.provider,
                appointment.start_date,
                appointment.start_time,
                appointment.end_time)

def repeat_appointments(provider, app):
    appointments = []
    instance = None
    while True:
        try:
            instance = Appointment.objects.get(id=app.id)
            break
        except Appointment.DoesNotExist:
            continue

    try:
        if instance.weekly_repeat is True and instance.number_of_weeks > 0:
            next_start_date = instance.start_date
            start_time = instance.start_time
            end_time = instance.end_time
            number_of_appointments = (instance.number_of_weeks * instance.weekly_repeat_gap)
            repeated_dates = []
            clients = instance.clients.all()
            next_start_date = next_start_date + timedelta(days=1)
            week_appointment_count = 0
            i = 0
            while i < number_of_appointments:
                provider_flag = validate_provider_in_booking_weekly_repeat(
                                                                           start_time, end_time,
                                                                           next_start_date.date())
                client_flag = validate_client_in_booking_weekly_repeat(clients, start_time, end_time,
                                                                       next_start_date.date())
                if provider_flag is False or client_flag is False:
                    next_start_date += timedelta(days=1)
                    continue
                week_start = next_start_date - timedelta(days=next_start_date.weekday())
                week_end = week_start + timedelta(days=6)
                if week_appointment_count >= instance.weekly_repeat_gap:
                    next_start_date = week_end + timedelta(days=1)
                    week_appointment_count = 0
                    continue
                week_appointment_count += 1
                i += 1
                repeated_dates.append(next_start_date)
                next_start_date += timedelta(days=1)

            for date in repeated_dates:
                start_date = date
                appointment = appointment_json(start_date, instance, i)
                repeating_telehealths_meetings_link(appointment)

                appointments.append(appointment)

        elif instance.select_dates is True:
            for date in instance.selected_dates:
                if instance.start_date.date() != date.date():

                    appointment = appointment_json(date, instance)
                    repeating_telehealths_meetings_link(appointment)

                    appointments.append(appointment)
        if len(appointments) > 0:
            appointments_created = Appointment.objects.bulk_create(appointments)

            appointment_services = instance.appointment_services.all()
            appointment_services = list(appointment_services)

            for appointment in appointments_created:
                for client in instance.clients.all():
                    appointment.clients.add(client)
                    appointment.save()

                for service in appointment_services:
                    AppointmentClientServices.objects.create(appointment=appointment, client=service.client,
                                                             service=service.service, fees=service.fees)


    except Exception as e:
        from_email = settings.CONTACT_EMAIL
        subject = 'Sorry for the inconvenience. Your repeating appointments could not be created'

        msg = EmailMultiAlternatives(subject=subject, from_email=from_email, to=[provider.email])
        try:
            msg.send()
        except Exception as ex:
            print(ex)


def repeat_bookings(provider, app, selected_dates=None):
    instance = None
    while True:
        try:
            instance = Appointment.objects.get(id=app.id)
            break
        except Appointment.DoesNotExist:
            continue

    try:
        room_prices = None
        booking_instance = instance.booking
        room = Room.objects.filter(id=booking_instance.room).first()
        location = room.location
        parent = booking_instance if booking_instance.is_recurrent_of is None else booking_instance.is_recurrent_of
        appointments = []
        bookings = []

        start_datetime = timezone.make_aware((datetime.combine(booking_instance.start_date.date(), booking_instance.start_time)),
                                             timezone.get_current_timezone())
        end_datetime = timezone.make_aware((datetime.combine(booking_instance.start_date.date(), booking_instance.end_time)),
                                           timezone.get_current_timezone())
        duration = (end_datetime - start_datetime).total_seconds()

        selected_dates = selected_dates if selected_dates else booking_instance.selected_dates
        for date in selected_dates:
            booking = Booking(
                start_date=date,
                start_time=booking_instance.start_time,
                end_time=booking_instance.end_time,
                room=booking_instance.room,
                is_recurrent_of=parent,
                booking_number=increment_booking_number(),
                select_dates=True,
                selected_dates=booking_instance.selected_dates
            )
            booking_created = None
            tiers = get_tiers(location, booking.start_date, duration)
            if room.tiers_excluded is False:
                total_price = Decimal(0.0)
                if booking_instance.room is not None:
                    room_prices = list(RoomPrice.objects.filter(
                        room=room.id))
                    for room_price in room_prices:
                        for tier in tiers:
                            if room_price.tier.tier == tier.get('tier'):
                                total_price += room_price.cost * \
                                               Decimal(tier.get('hours'))

                room_cost = total_price
                booking_dict = booking.__dict__
                booking_dict.pop('_state', None)

                booking_created = Booking.objects.create(**booking_dict)
                if room_prices is not None:
                    for room_price in room_prices:
                        for tier in tiers:
                            if room_price.tier.tier == tier.get('tier'):
                                BookingPrice.objects.create(
                                    booking=booking_created,
                                    tier=tier['tier'],
                                    hours=tier['hours'],
                                    price=room_price.cost)
            else:
                booking_dict = booking.__dict__
                booking_dict.pop('_state', None)

                booking_created = Booking.objects.create(**booking_dict)
                room_cost = Decimal(0.0)
                hours = 0
                for tier in tiers:
                    hours = hours + tier['hours']
                BookingPrice.objects.create(
                    booking=booking,
                    hours=hours,
                    price=Decimal(0.0))

            booking_created.room_cost = room_cost
            booking_created.save()

            bookings.append(booking_created)
            with schema_context('public'):
                AdminBookingDetails.objects.create(id=booking_created.id, start_date=booking.start_date,
                                                   start_time=booking.start_time, end_time=booking.end_time,
                                                   location=room.location, room=room,
                                                   room_cost=booking.room_cost,
                                                   status=booking.status, provider=provider.provider,
                                                   )

        for booking in bookings:
            appointment = Appointment(
                booking=booking,
                start_date=booking.start_date,
                start_time=instance.start_time,
                end_time=instance.end_time,
                telehealth=instance.telehealth,
                google=instance.google,
                zoom=instance.zoom,
                telehealth_zoom_id=None,
                telehealth_google_id=None,
            )
            repeating_telehealths_meetings_link(appointment)

            appointments.append(appointment)

        appointments_created = Appointment.objects.bulk_create(appointments)

        appointment_services = instance.appointment_services.all()
        appointment_services = list(appointment_services)

        for appointment in appointments_created:
            for client in instance.clients.all():
                appointment.clients.add(client)
                appointment.save()

            for service in appointment_services:
                AppointmentClientServices.objects.create(appointment=appointment, client=service.client,
                                                         service=service.service, fees=service.fees)
        with schema_context('public'):
            for appointment in appointments_created:
                booking = appointment.booking
                admin_appointment = AdminBookingDetails.objects.filter(id=booking.id).first()
                admin_appointment.appointments_start_time = app.start_time
                admin_appointment.appointments_end_time = app.end_time
                admin_appointment.save()

    except Exception as e:
        from_email = settings.CONTACT_EMAIL
        subject = 'Sorry for the inconvenience. Your repeating appointments could not be created'

        msg = EmailMultiAlternatives(subject=subject, from_email=from_email, to=[provider.email])
        try:
            msg.send()
        except Exception as ex:
            print(ex)
